package com.weifly.weistock.optionvix.impl;

import com.weifly.weistock.core.util.visitor.PreviousVisitor;
import com.weifly.weistock.optionvix.OptionVixConstant;
import com.weifly.weistock.optionvix.bo.VixDayBO;
import com.weifly.weistock.optionvix.bo.VixYearBO;
import com.weifly.weistock.optionvix.dto.VixRegionDTO;
import com.weifly.weistock.optionvix.dto.VixSummaryDTO;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

/**
 * VIX统计计算器
 *
 * @author weifly
 * @since 2021/1/25
 */
public class VixSummaryCalculator {

    private static final double VIX_GAP = 0.5;
    private static final double MIN_LOW = 3;

    private Collection<VixYearBO> yearList; // 原始数据集合

    private VixDayBO newestDay; // 最新天

    // recent相关
    private int recentDayNumber; // 收录天数
    private int recentSmallNumber; // 比newestVix小的天数
    private VixDayBO recentStartDay; // 开始日期
    private VixDayBO recentEndDay; // 结束日期

    // all相关
    private int allDayNumber; // 收录天数
    private int allSmallNumber; // 比newestVix小的天数
    private VixDayBO allStartDay; // 开始日期
    private VixDayBO allEndDay; // 结束日期

    public VixSummaryCalculator(Collection<VixYearBO> yearList) {
        this.yearList = yearList;
    }

    public VixSummaryDTO calc() {
        VixSummaryDTO summaryDTO = new VixSummaryDTO();
        // 初始化regionList
        initVixRegionList(summaryDTO);
        // 遍历dayList
        visitVixDayList(summaryDTO);
        // 计算region值
        calcRegionValue(summaryDTO);
        // 收集数据
        collectValue(summaryDTO);
        return summaryDTO;
    }

    private void initVixRegionList(VixSummaryDTO summaryDTO) {
        summaryDTO.setRegionList(new ArrayList<>());
        double high = 100;
        for (double low = high - VIX_GAP; low >= MIN_LOW; high = low, low = low - VIX_GAP) {
            VixRegionDTO vixRegion = new VixRegionDTO();
            vixRegion.setHighVix(high);
            vixRegion.setLowVix(low);
            summaryDTO.getRegionList().add(vixRegion);
        }
    }

    private void visitVixDayList(VixSummaryDTO summaryDTO) {
        PreviousVisitor<VixDayBO> iter = new PreviousVisitor(this.yearList);
        while (iter.hasNext()) {
            VixDayBO vixDay = iter.next();
            if (this.newestDay == null) {
                this.newestDay = vixDay;
            }
            putRecent(summaryDTO, vixDay);
            putAll(summaryDTO, vixDay);
        }
    }

    private void putRecent(VixSummaryDTO summaryDTO, VixDayBO vixDay) {
        if (this.recentDayNumber >= OptionVixConstant.MAX_RECENT_DAY_NUMBER) {
            // recent取样点已满
            return;
        }

        this.recentDayNumber++;
        if (this.newestDay.getCloseValue() > vixDay.getCloseValue()) {
            this.recentSmallNumber++;
        }
        this.recentStartDay = vixDay;
        if (this.recentEndDay == null) {
            this.recentEndDay = vixDay;
        }
        for (VixRegionDTO vixRegion : summaryDTO.getRegionList()) {
            if (vixRegion.getHighVix() > vixDay.getCloseValue() && vixDay.getCloseValue() >= vixRegion.getLowVix()) {
                vixRegion.setRecentDayNumber(vixRegion.getRecentDayNumber() + 1);
            }
        }
    }

    private void putAll(VixSummaryDTO summaryDTO, VixDayBO vixDay) {
        this.allDayNumber++;
        if (this.newestDay.getCloseValue() > vixDay.getCloseValue()) {
            this.allSmallNumber++;
        }
        this.allStartDay = vixDay;
        if (this.allEndDay == null) {
            this.allEndDay = vixDay;
        }
        for (VixRegionDTO vixRegion : summaryDTO.getRegionList()) {
            if (vixRegion.getHighVix() > vixDay.getCloseValue() && vixDay.getCloseValue() >= vixRegion.getLowVix()) {
                vixRegion.setAllDayNumber(vixRegion.getAllDayNumber() + 1);
            }
        }
    }

    private void calcRegionValue(VixSummaryDTO summaryDTO) {
        // 排除前面、后面dayNumber=0的region
        List<VixRegionDTO> regionList = summaryDTO.getRegionList();
        int startIndex = regionList.size() - 1;
        for (int i = 0; i < regionList.size(); i++) {
            VixRegionDTO vixRegion = regionList.get(i);
            if (vixRegion.getRecentDayNumber() > 0 || vixRegion.getAllDayNumber() > 0) {
                startIndex = i;
                break;
            }
        }
        int endIndex = 0;
        for (int i = regionList.size() - 1; i >= 0; i--) {
            VixRegionDTO vixRegion = regionList.get(i);
            if (vixRegion.getRecentDayNumber() > 0 || vixRegion.getAllDayNumber() > 0) {
                endIndex = i;
                break;
            }
        }
        if (startIndex > endIndex) {
            // dayNumber都是0，不再计算
            return;
        }

        // 计算统计值
        regionList = regionList.subList(startIndex, endIndex + 1);
        summaryDTO.setRegionList(regionList);
        BigDecimal recentDayNumber = new BigDecimal(this.recentDayNumber); // recent-总天数
        int recentTotalDayNumber = 0; // recent-累积天数
        BigDecimal allDayNumber = new BigDecimal(this.allDayNumber); // all-总天数
        int allTotalDayNumber = 0; // all-累积天数
        for (int i = regionList.size() - 1; i >= 0; i--) {
            VixRegionDTO vixRegion = regionList.get(i);
            if (vixRegion.getRecentDayNumber() > 0 && this.recentDayNumber > 0) {
                // recent-天数百分比
                vixRegion.setRecentDayPercent(compute(vixRegion.getRecentDayNumber(), recentDayNumber));
                // recent-累积天数、累积百分比
                recentTotalDayNumber += vixRegion.getRecentDayNumber();
                vixRegion.setRecentTotalDayNumber(recentTotalDayNumber);
                vixRegion.setRecentTotalDayPercent(compute(recentTotalDayNumber, recentDayNumber));
            }
            if (vixRegion.getAllDayNumber() > 0 && this.allDayNumber > 0) {
                // all-天数百分比
                vixRegion.setAllDayPercent(compute(vixRegion.getAllDayNumber(), allDayNumber));
                // all-累积天数、累积百分比
                allTotalDayNumber += vixRegion.getAllDayNumber();
                vixRegion.setAllTotalDayNumber(allTotalDayNumber);
                vixRegion.setAllTotalDayPercent(compute(allTotalDayNumber, allDayNumber));
            }
        }
    }

    private double compute(int num1, BigDecimal num2) {
        return new BigDecimal(num1 * 100).divide(num2, 2, RoundingMode.HALF_UP).doubleValue();
    }

    private void collectValue(VixSummaryDTO summaryDTO) {
        if (this.newestDay != null) {
            summaryDTO.setNewestDay(this.newestDay.getDay());
            summaryDTO.setNewestVix(this.newestDay.getCloseValue());
        }
        summaryDTO.setRecentDayNumber(this.recentDayNumber);
        if (this.recentStartDay != null) {
            summaryDTO.setRecentStartDay(this.recentStartDay.getDay());
        }
        if (this.recentEndDay != null) {
            summaryDTO.setRecentEndDay(this.recentEndDay.getDay());
        }
        if (this.recentDayNumber > 0) {
            summaryDTO.setRecentPercent(compute(this.recentSmallNumber + 1, new BigDecimal(this.recentDayNumber)));
        }
        summaryDTO.setAllDayNumber(this.allDayNumber);
        if (this.allStartDay != null) {
            summaryDTO.setAllStartDay(this.allStartDay.getDay());
        }
        if (this.allEndDay != null) {
            summaryDTO.setAllEndDay(this.allEndDay.getDay());
        }
        if (this.allDayNumber > 0) {
            summaryDTO.setAllPercent(compute(this.allSmallNumber + 1, new BigDecimal(this.allDayNumber)));
        }
    }
}
